package com.tau.egonetwork.gui.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import com.tau.egonetwork.gui.Environment;
import com.tau.egonetwork.profiles.Group;
import com.tau.egonetwork.profiles.MainProfile;
import com.tau.egonetwork.profiles.Profile;

public class GroupEditDialog<T extends Comparable<T>> extends BasicDialog {

	protected Group<T> group;
	protected MainProfile<T> mainProfile;
	
	private final boolean editMode;
	private boolean changesWereMadeToGroup = false;
	
	private Text nameText;
	
	private ArrayList<Profile<T>> nonMembersArrayList;
	private ArrayList<Profile<T>> membersArrayList;
	
	/* variables handling search in one of the lists: */
	private HashMap<Integer, Integer> membersListSearchResults = new HashMap<Integer, Integer>();
	private HashMap<Integer, Integer> nonMembersListSearchResults = new HashMap<Integer, Integer>();
	private boolean membersSearchActive = false;
	private boolean nonMembersSearchActive = false;
	private String membersSearchText = null;
	private String nonMembersSearchText = null;
	
	//hold a list (at first empty) of changes made to group members (for edit mode).
	//save changes when OK is clicked:
	final ArrayList<Profile<T>> groupMembersChanges = new ArrayList<Profile<T>>();
	
	/* constructor for editing: */
	public GroupEditDialog(Shell parent, Group<T> group, MainProfile<T> mainProfile) {
		super(parent);
		this.group = group;
		this.mainProfile = mainProfile;
		this.editMode = true;
	}
		
	/* constructor for new: */
	public GroupEditDialog(Shell parent, MainProfile<T> mainProfile) {
		super(parent);
		this.mainProfile = mainProfile;
		this.editMode = false;
	}


	@Override
	public Object setReturnValue() {
		return changesWereMadeToGroup;
	}
	
	@Override
	protected void createWindowContents() {
		
		Composite nameComposite = new Composite(shell, SWT.NONE);
		nameComposite.setLayout(new GridLayout(2, false));
		nameComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
		
		Label nameLabel = new Label(nameComposite, SWT.NONE);
		nameLabel.setText("Name: ");
		nameLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
		
		nameText = new Text(nameComposite, SWT.BORDER | SWT.FLAT);
		GridData nameTextLayoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
		nameTextLayoutData.widthHint = 460;
		nameText.setLayoutData(nameTextLayoutData);

		Composite groupMembersComposite = new Composite(shell, SWT.NONE);
		groupMembersComposite.setLayout(new GridLayout(3, false));
		groupMembersComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		Label membersLabel = new Label(groupMembersComposite, SWT.NONE);
		membersLabel.setText("Friends in Group: ");
		GridData membersLabelGridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
		membersLabelGridData.horizontalSpan = 2;
		membersLabel.setLayoutData(membersLabelGridData);
		
		Label nonMembersLabel = new Label(groupMembersComposite, SWT.NONE);
		nonMembersLabel.setText("Rest of Friends: ");
		nonMembersLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
		
		final List membersList = new List(groupMembersComposite, SWT.BORDER | SWT.V_SCROLL | SWT.MULTI);
		GridData membersListGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
		membersListGridData.heightHint = 320;
		membersListGridData.widthHint = 250;
		membersList.setLayoutData(membersListGridData);
		
		Composite buttonsBetweenListsComposite = new Composite(groupMembersComposite, SWT.NONE);
		GridLayout buttonsBetweenListsGridLayout = new GridLayout(1, true);
		buttonsBetweenListsGridLayout.marginWidth = 0;
		buttonsBetweenListsComposite.setLayout(buttonsBetweenListsGridLayout);
		buttonsBetweenListsComposite.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
		
		final Button leftToRightButton = new Button(buttonsBetweenListsComposite, SWT.PUSH | SWT.FLAT);
		leftToRightButton.setText("-->");

		final Button rightToLeftButton = new Button(buttonsBetweenListsComposite, SWT.PUSH | SWT.FLAT);
		rightToLeftButton.setText("<--");

		final List nonMembersList = new List(groupMembersComposite, SWT.BORDER | SWT.V_SCROLL | SWT.MULTI);
		GridData nonMembersListGridData = new GridData(SWT.FILL, SWT.FILL, true, true);
		nonMembersList.setLayoutData(nonMembersListGridData);
		nonMembersListGridData.heightHint = 320;
		nonMembersListGridData.widthHint = 250;
		
		Composite membersListSearchComposite = new Composite(groupMembersComposite, SWT.NONE);
		GridLayout membersListSearchCompositeLayout = new GridLayout(3, false);
		membersListSearchCompositeLayout.marginTop = 0;
		membersListSearchCompositeLayout.marginWidth = 0;
		membersListSearchComposite.setLayout(membersListSearchCompositeLayout);
		membersListSearchComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		
		final Text membersListSearchText = new Text(membersListSearchComposite, SWT.BORDER | SWT.FLAT);
		membersListSearchText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		membersListSearchText.setTextLimit(50);
		membersListSearchText.setFocus();
		
		final Button membersListSearchButton = new Button(membersListSearchComposite, SWT.PUSH | SWT.FLAT);
		membersListSearchButton.setImage(Environment.ICON_SEARCH);
		membersListSearchButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
		membersListSearchButton.setEnabled(false);
		
		final Button membersListClearButton = new Button(membersListSearchComposite, SWT.PUSH | SWT.FLAT);
		membersListClearButton.setText("Clear");
		membersListClearButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
		membersListClearButton.setEnabled(false);
		
		//empty label (for the "buttonsBetweenList" column):
		new Label(groupMembersComposite, SWT.NONE);
		
		Composite nonMembersListSearchComposite = new Composite(groupMembersComposite, SWT.NONE);
		GridLayout nonMembersListSearchCompositeLayout = new GridLayout(3, false);
		nonMembersListSearchCompositeLayout.marginTop = 0;
		nonMembersListSearchCompositeLayout.marginWidth = 0;
		nonMembersListSearchComposite.setLayout(nonMembersListSearchCompositeLayout);
		nonMembersListSearchComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		
		final Text nonMembersListSearchText = new Text(nonMembersListSearchComposite, SWT.BORDER | SWT.FLAT);
		nonMembersListSearchText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		nonMembersListSearchText.setTextLimit(50);
		nonMembersListSearchText.setFocus();
		
		final Button nonMembersListSearchButton = new Button(nonMembersListSearchComposite, SWT.PUSH | SWT.FLAT);
		nonMembersListSearchButton.setImage(Environment.ICON_SEARCH);
		nonMembersListSearchButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
		nonMembersListSearchButton.setEnabled(false);
		
		final Button nonMembersListClearButton = new Button(nonMembersListSearchComposite, SWT.PUSH | SWT.FLAT);
		nonMembersListClearButton.setText("Clear");
		nonMembersListClearButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
		nonMembersListClearButton.setEnabled(false);

		//add profile's friends and populate list with them:
		nonMembersArrayList = new ArrayList<Profile<T>>();
		membersArrayList = new ArrayList<Profile<T>>();
	
		if (mainProfile.getFriendsList() != null) {
			for (Profile<T> p : mainProfile.getFriendsList()) {
				//if not in edit mode - add all friends
				//if in edit mode, only add friends not in group
				if (!editMode || !group.contains(p)) {
					nonMembersArrayList.add(p);
				} else {
					membersArrayList.add(p);
				}
			}
		}
		
		for (Profile<T> p : nonMembersArrayList) {
			nonMembersList.add(p.getName());
		}
		
		if (membersArrayList.size() == 0)
			leftToRightButton.setEnabled(false);
		
		if (nonMembersArrayList.size() == 0)
			rightToLeftButton.setEnabled(false);
				
		membersListSearchText.addListener(SWT.KeyUp, new Listener() {
			
			@Override
			public void handleEvent(Event event) {
				membersListSearchButton.setEnabled(!membersListSearchText.getText().trim().equals(""));
			}
		});
		
		membersListSearchButton.addListener(SWT.Selection, new Listener() {
			
			@Override
			public void handleEvent(Event event) {
				conductSearch(membersListSearchText.getText(), membersList,
						membersArrayList, membersListSearchResults);
				membersSearchActive = true;
				membersListClearButton.setEnabled(true);
				membersSearchText = membersListSearchText.getText();
			}
		});
		
		membersListClearButton.addListener(SWT.Selection, new Listener() {

			@Override
			public void handleEvent(Event event) {
				clearSearch(membersList, membersArrayList);
				membersSearchActive = false;
				membersListSearchButton.setEnabled(false);
				membersListClearButton.setEnabled(false);
				membersListSearchText.setText("");
			}
			
		});
				
		leftToRightButton.addListener(SWT.Selection, new Listener() {
			
			@Override
			public void handleEvent(Event event) {
				handleSwitchSide(membersList, nonMembersList, membersArrayList, nonMembersArrayList,
						membersSearchActive, nonMembersSearchActive, membersListSearchResults,
						nonMembersListSearchResults, nonMembersSearchText); 
				leftToRightButton.setEnabled(membersArrayList.size() > 0);
				rightToLeftButton.setEnabled(nonMembersArrayList.size() > 0);
			}
		});
		
		rightToLeftButton.addListener(SWT.Selection, new Listener() {
			
			@Override
			public void handleEvent(Event event) {
				handleSwitchSide(nonMembersList, membersList, nonMembersArrayList, membersArrayList,
						nonMembersSearchActive, membersSearchActive, nonMembersListSearchResults,
						membersListSearchResults, membersSearchText);
				rightToLeftButton.setEnabled(nonMembersArrayList.size() > 0);
				leftToRightButton.setEnabled(membersArrayList.size() > 0);
			}
		});

		nonMembersListSearchText.addListener(SWT.KeyUp, new Listener() {
			
			@Override
			public void handleEvent(Event event) {
				nonMembersListSearchButton.setEnabled(!nonMembersListSearchText.getText().trim().equals(""));
			}
		});
		
		nonMembersListSearchButton.addListener(SWT.Selection, new Listener() {
			
			@Override
			public void handleEvent(Event event) {
				conductSearch(nonMembersListSearchText.getText(), nonMembersList,
						nonMembersArrayList, nonMembersListSearchResults);
				nonMembersSearchActive = true;
				nonMembersListClearButton.setEnabled(true);
				nonMembersSearchText = nonMembersListSearchText.getText();
			}
		});
		
		nonMembersListClearButton.addListener(SWT.Selection, new Listener() {

			@Override
			public void handleEvent(Event event) {
				clearSearch(nonMembersList, nonMembersArrayList);
				nonMembersSearchActive = false;
				nonMembersListSearchButton.setEnabled(false);
				nonMembersListClearButton.setEnabled(false);
				nonMembersListSearchText.setText("");
			}
			
		});
		
		if (editMode) {
			
			nameText.setText(group.getGroupName());
			
			for (Profile<T> p : membersArrayList) {
				membersList.add(p.getName());
			}
		}
		
	}
	
	private void handleSwitchSide(List removingFromList, List addingToList,
			ArrayList<Profile<T>> removingFromArrayList, ArrayList<Profile<T>> addingToArrayList,
			boolean removingFromActiveSearch, boolean addingToActiveSearch, 
			HashMap<Integer, Integer> removingFromSearchResults, HashMap<Integer, Integer> addingToSearchResults,
			String addingToSearchText) {
		
		int[] selectedIndices = removingFromList.getSelectionIndices();
		
		if (selectedIndices.length == 0)
			return;
		
		Arrays.sort(selectedIndices);
		
		//it would be wise to go over the indices in descending order,
		//since we'll be removing items and indices might change.
		int[] descendingOrder = new int[selectedIndices.length];
		for (int i = 0; i < selectedIndices.length; i++) {
			descendingOrder[i] = selectedIndices[selectedIndices.length - 1 -i];
		}
		
		int highlightCount = 0;
		
		//now we have it in descending order, remove it:
		for (int i : descendingOrder) {
			
			Profile<T> profileSwitchingSide;
			if (removingFromActiveSearch) {
				profileSwitchingSide = removingFromArrayList.get(removingFromSearchResults.get(i));
				removingFromSearchResults.remove(i);
			}
			else {
				profileSwitchingSide = removingFromArrayList.get(i);
			}
			
			addingToArrayList.add(profileSwitchingSide);
			
			if (addingToActiveSearch) {
				if (profileSwitchingSide.getName().toLowerCase().startsWith(addingToSearchText.toLowerCase())) {
					addingToList.add(profileSwitchingSide.getName());
					addingToSearchResults.put(addingToSearchResults.size() - 1, addingToArrayList.size() - 1);
					highlightCount++;
				}
			} else {
				addingToList.add(profileSwitchingSide.getName());
				highlightCount++;
			}
			
			removingFromArrayList.remove(profileSwitchingSide);
			removingFromList.remove(i);
						
			/* save change to groupMembersChanges array. Notice these changes
			 * are symmetric - if a user has switched side and even number of
			 * times (0, 2, 4, so on), he remains unchanged in relation to
			 * group membership status when the dialog opened! */
			if (groupMembersChanges.contains(profileSwitchingSide)) {
				groupMembersChanges.remove(profileSwitchingSide);
			} else {
				groupMembersChanges.add(profileSwitchingSide);
			}
		}
		
		if (highlightCount > 0)
			addingToList.setSelection(addingToList.getItemCount() - highlightCount, addingToList.getItemCount() - 1);
	}
	
	private void conductSearch(String searchText, List searchList,
			ArrayList<Profile<T>> searchArrayList, HashMap<Integer, Integer> searchResults) {
		
		//first of all clear the list of contents:
		searchList.removeAll();
		
		//make sure search result is empty as well:
		searchResults.clear();
		
		//go over searchArrayList, search prefix of searchText
		//and populate searchList accordingly:
		
		int new_i = 0;
		for (int i = 0; i < searchArrayList.size(); i++) {
			if (searchArrayList.get(i).getName().toLowerCase().startsWith(searchText.toLowerCase())) {
				searchList.add(searchArrayList.get(i).getName());
				searchResults.put(new_i++, i);
			}
		}
		
	}
	
	private void clearSearch(List clearedList, ArrayList<Profile<T>> populateWithArrayList) {
		
		//clear the list:
		clearedList.removeAll();
		
		for (int i = 0; i < populateWithArrayList.size(); i++) {
			clearedList.add(populateWithArrayList.get(i).getName());
		}
		
	}

	@Override
	protected void onOKClicked() {
		if (nameText.getText() == null || nameText.getText().trim().equals("")) {
			MessageBox mbox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
			mbox.setText("Group Details Error");
			mbox.setMessage("Group name cannot be empty!");
			mbox.open();
			return;
		}

		if (editMode) {
			
			String dialogName = nameText.getText();
			if (!dialogName.trim().equals(group.getGroupName().trim())) {
				group.setGroupName(dialogName.trim());
				changesWereMadeToGroup = true;
			}

			if (groupMembersChanges.size() > 0) {
				
				for (Profile<T> p : groupMembersChanges) {
					if (group.contains(p)) {
						group.remove(p);
					} else {
						group.add(p);
					}
				}
				
				changesWereMadeToGroup = true;
				
			}
			
		} else {
							
			Group<T> newGroup = new Group<T>(nameText.getText().trim());
			
			if (membersArrayList.size() > 0) {
				for (Profile<T> p : membersArrayList) {
					newGroup.add(p);
				}
			}

			if (mainProfile.getGroupsList() == null)
				mainProfile.createGroupsList();

			mainProfile.getGroupsList().add(newGroup);
			
			changesWereMadeToGroup = true;
			
		}
		
		shell.close();
	}
}
